/**
 * 
 */
package epsofts.Neutron.mesures;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.HashSet;
import java.util.Set;
import java.util.Map.Entry;

import epsofts.Neutron.Plateau;
import epsofts.Neutron.eCase;


/**
 * @author Pierre ESCALLIER
 *
 */
public class AdapteurMesures
{
	final static HashSet<String> set = new HashSet<String>(200, (float) 0.75);
	final static StringBuilder sb = new StringBuilder(80);
	
	final static String ROUGE	= "1";
	final static String BLEUE	= "-1";
	final static String NEUTRON	= "1";
	final static String VIDE		= "0";
	
	static int FACTOR = 1;
	static double LIMITE = 0.20;
	static String FILENAME_MESURES = "mesures.csv";
	static String FILENAME_VALIDATION = "validation.csv";
	
	static float somme = 0;
	static int compte = 0;
	static int compte_sup = 0;
	static int compte_doubles = 0;
	
	static boolean fusion = false;
	
	final static BufferedReader FromKeyboard = new BufferedReader(new InputStreamReader(System.in));
	
	/**
	 * @param args
	 */
	public static void main(String[] args)
	{
		boolean generate_validation_measures = false;
		
		for(int i=0; (i < args.length); ++i)
		{			
			if (args[i].compareToIgnoreCase("-DIR") == 0)
			{
				TestMain.REPERTOIRE = args[i+1];
			}
			
			if (args[i].compareToIgnoreCase("-LIMITE") == 0)
			{
				LIMITE = Integer.parseInt(args[i+1]);
			}
			
			if (args[i].compareToIgnoreCase("-FUSION") == 0)
			{
				fusion = true;
			}
			
			if (args[i].compareToIgnoreCase("-BASIC") == 0)
			{
				generate_validation_measures = true;
			}
			
			if (args[i].compareToIgnoreCase("-FACTOR") == 0)
			{
				FACTOR = Integer.parseInt(args[i+1]);
			}
			
		}
		
		File fichier_resultat = null;
		File fichier_validation = null;
		if ((fusion) || (!TestMain.tournoi_ouvert))
		{
			fichier_resultat = new File(TestMain.REPERTOIRE, FILENAME_MESURES);
			fichier_validation = new File(TestMain.REPERTOIRE, FILENAME_VALIDATION);
		}
		else if (TestMain.tournoi_ouvert)
		{
			fichier_resultat = new File(TestMain.repertoire_session, FILENAME_MESURES);
			fichier_validation = new File(TestMain.repertoire_session, FILENAME_VALIDATION);
		}

		PrintStream sortie = null;
		PrintStream sortie_validation = null;
		try
		{
			sortie = new PrintStream(fichier_resultat);
			sortie_validation = new PrintStream(fichier_validation);
		}
		catch(FileNotFoundException e)
		{
			System.out.println("Impossible de créer le fichier de sortie");
			return;
		}				
		
		
		if (generate_validation_measures)
		{						
			genererValidationMeasures(sortie_validation);
			return;
		}
		
		int no_session = 1;
		int no_partie = 1;
		File fichier = null;
		File repertoire = null;
		String ligne = null;
		BufferedReader br = null;
		do
		{
			if (fusion)
			{
				repertoire = new File(String.format("%s"+File.separator+"%s%d"+File.separator+"", TestMain.REPERTOIRE, TestMain.SESSION_PREFIX, no_session));
			}
			else if (TestMain.tournoi_ouvert)
			{
				repertoire = new File(TestMain.repertoire_session.getAbsolutePath());
			}
			else
			{
				repertoire = new File(TestMain.REPERTOIRE);
			}
			
			if (repertoire.exists())
			{
				no_partie = 1;
				do
				{
					fichier = new File(repertoire, String.format("%s%d.txt", TestMain.PARTIE_PREFIX, no_partie));
					
					if ((fichier != null) && (fichier.exists()))
					{
						try
						{
							br = new BufferedReader(new FileReader(fichier));
						}
						catch(FileNotFoundException e)
						{
							System.out.println(String.format("Le fichier '%s' ne peut pas être ouvert.", fichier.getName()));
							continue;
						}
						
						do
						{
							try
							{
								ligne = br.readLine();
							}
							catch(IOException e)
							{
								System.out.println("Exception readLine: " + e.getMessage());
								ligne = null;
							}
							
							if (ligne != null)
							{
								ConvertirFormat(ligne, sortie, sortie_validation);
							}
						}while(ligne != null);
					}
					++no_partie;
					System.out.print('.');
				}while((fichier != null) && (fichier.exists()));
				
				++no_session;
				System.out.println();
			}
		}while(fusion && repertoire.exists());
			
		System.out.println(String.format("Nb de plateaux différents : %d", set.size()));
		System.out.println(String.format("Score rms : %03.2f \t Score > %d%% : %d \t Doubles : %d \t Total : %d", ((float) somme / (float) compte), (int) (LIMITE*100), compte_sup, compte_doubles, compte_sup - compte_doubles));
	}

	private static void genererValidationMeasures(PrintStream sortie)
	{
		HashSet<Entry<Plateau,eCase>> results = GenerateurPlateaux.genererPlateauxQuasiFinaux(50);
		
		for(Entry<Plateau, eCase> e : results)
		{
			EcrirePlateau(e.getKey(), e.getValue() == eCase.ROUGE ? 1 : -1, sortie, sortie);
		}
		/* TODO:
		On souhaite générer des plateaux (normalisés) représentant un état où l'issue de la partie ne fait plus de doute (quelque soit le vainqueur).
		
		Pour i allant de 0 à MAX_GEN Faire
		
			cases[25] = GenererCasesVides()
			nbRouges = 0, nbBleues = 0;
			
			neutron = TirerPositionNeutronAuHasard() // Parmis les 9 positions possibles d'un plateau normalisé.
			
			typeSituation = TirerTypeAuHasard() // "neutron blocable", "neutron libre vers victoire", "neutron forcé vers défaite"
			directionChoisie = ChoisirDirection(neutron, cas) // vers victoire, vers défaite, n'importe
			boolean bouchon = false; 
			
			Tantque(nbRouges != 5 ET nbBleues != 5) Faire
							
				Selon typeSituation Faire
				
					Cas "neutron blocable":
					
						Si (DegréDeLiberté(neutron) > 1) Alors
							nouvelleBille = BloquerUneNouvelleDirectionDuNeutron(neutron, directionChoisie); // On laisse libre la direction choisie
							couleur = ChoisirCouleurBilleAuHasard(nbRouges-1, nbBleues) // Garder 1 bille rouge en réserve pour le bouchon
						Sinon Si (!bouchon)
							nouvelleBille = GenererBouchon(neutron, directionChoisie) // Direction du dernier degré de liberté, à > 1 de distance.
							couleur = ROUGE
							bouchon = true;
						Sinon
							nouvelleBille = GenererNouvelleBilleHorsDirection(neutron, directionChoisie);
							couleur = ChoisirCouleurBilleAuHasard(nbRouges, nbBleues)
						FSi												
					
					Cas "neutron libre vers victoire":
					Cas "neutron forcé vers défaite":										
						
						Faire
							nouvelleBille = TirerPositionBilleAuHasard() // Tire une position libre.
						TantQue(ObstrueDirection(neutron, nouvelleBille, directionChoisie);
						
						couleur = ChoisirCouleurBilleAuHasard(nbRouges, nbBleues)
					
					Autre:
				
				FSelon
								
				IncrémenterCouleurBille(nbRouges, nbBleues)
				cases[nouvelleBille] = couleur
			
			FTantque
			
		
		FPour
		 */
	}
	
	private static Plateau getPlateauFromString(String s)
	{
		eCase[] cases = new eCase[25];
		eCase turn = eCase.VIDE;
		for(int i=0; i < 27; ++i)
		{
			if (i < 25) cases[i] = (s.charAt(i) == 'r' ? eCase.ROUGE : s.charAt(i) == 'b' ? eCase.BLEUE : s.charAt(i) == 'n' ? eCase.NEUTRON : eCase.VIDE);
			else if (i > 25) turn = (s.charAt(i) == 'R' ? eCase.ROUGE : eCase.BLEUE); 
		}
		
		return new Plateau(cases, turn, (short) 0, null);
	}
	
	private static Plateau previousBoard = null, previousPreviousBoard = null;
	private static void ConvertirFormat(final String ligne, final PrintStream sortie, final PrintStream sortie_validation)
	{
		float score = Float.parseFloat(ligne.substring(28).replace(',', '.'));
		
		compte++;
		somme += Math.abs(score);
		if (Math.abs(score) > LIMITE)
		{
			compte_sup++;
		}
		else
		{
			previousBoard = null;
			previousPreviousBoard = null;
			return;
		}
		
		Plateau p = getPlateauFromString(ligne);
		
		EcrirePlateau(p, score, sortie, sortie_validation);
		previousPreviousBoard = previousBoard;
		previousBoard = p;
	}
	
	private static void EcrirePlateau(Plateau p, double score, PrintStream sortie, PrintStream sortie_validation)
	{	
		p = p.normaliser(eCase.ROUGE, true);
		
		eCase vainqueur = p.testerPlateauTerminal(true);
		if (vainqueur != eCase.VIDE)
		{						
			score = vainqueur == eCase.ROUGE ? 1 : -1;
			sortie = sortie_validation;
			
			EcriturePlateau(previousBoard, score*-1, sortie_validation);
			if (previousPreviousBoard.listerCoups().size() == 1)
			{
				EcriturePlateau(previousPreviousBoard, score, sortie_validation);
			}
			
			return; // On ne note pas les plateaux terminaux.
		}
		
		EcriturePlateau(p, score, sortie);
	}
	
	private static void EcriturePlateau(Plateau p, double score, PrintStream sortie)
	{
		sb.setLength(0);
		
		for(eCase c : p.getCases())
		{
			sb.append(c == eCase.BLEUE ? BLEUE : c == eCase.ROUGE ? ROUGE : VIDE );
			sb.append(",");
		}
		
		// Le neutron est compris dans une matrice 3x3 au centre gauche du plateau, conversion des coordonées 5x5 -> 3x3
		int a = p.getNeutron();
		int y = (((int) (a / 5))-1);
		int x = a - (5*y) - 5;
		int ninput = (3*y) + (x%3);
		
		for(int i=0; i < 9; ++i)
		{
			sb.append(i == ninput ? NEUTRON : VIDE);
			sb.append(",");
		}
		
		//sb.append(String.format("%s;%d;%d;%d;%d;%d;%d;", (p.isNeutronForce() ? "1":"0"), p.getNbBillesMobiles(), p.getNbBillesMobilesAdv(), p.getNbDirectionsPossibles(), p.getNbDirectionsPossiblesAdv(), p.getNbCasesVictoiresLibres(), p.getNbCasesDefaitesLibres()));
		
		//sb.append(ligne.substring(0, 25).replace("r", ROUGE+";").replace("b", BLEUE+";").replace("n", NEUTRON+";").replace(".",VIDE+";"));		
		
		sb.append(String.valueOf(score*FACTOR));
		
		if (!set.contains(sb.toString()))
		{
			set.add(sb.toString());
			sortie.println(sb);
		}
		else
		{
			compte_doubles++;
		}
	}

}
